9 循环语句 股价涨跌幅提醒
9.1 引言循环在批量金融数据处理中的价值
金融市场产生海量的时间序列数据——股价、成交量、收益率等。循环结构使我们能够高效地处理这些重复数据,自动执行重复性任务,是构建金融分析系统的核心工具。
9.2 本章学习目标
通过本章学习,你将能够:
- 使用
for循环与range()遍历价格与涨跌幅序列,累计计算区间收益 - 使用
while循环按条件迭代,说明它与for的适用差异及避免死循环的要点 - 使用
break与continue控制循环流程,并解释for-else/while-else中else子句的触发条件 - 在循环中组合条件判断,实现股价涨跌幅预警等批量监控任务
- 对比 for 循环、列表推导式、
zip()与 NumPy 向量化的性能差异,选择合适的批量处理方式
先修:章节 1(第1章 常用基本数据类型)、章节 2(第2章 列表、字典和元组)、章节 3(第3章 函数定义 算术平均收益率计算)、章节 4(第4章 常用内置函数)、章节 5(第5章 NumPy 数组的创建与访问)、章节 6(第6章 NumPy 数组运算)、章节 7(第7章 NumPy 矩阵处理)
9.3 For循环基础
理论背景:迭代与递归
从计算理论来看,循环实现了迭代(Iteration)模式:
- 迭代: 通过重复执行相同操作处理数据集合
- 递归: 函数调用自身解决问题
- 对比: 循环通常更高效,递归更优雅
在金融数据处理中,迭代更为常见,因为:
- 金融数据天然具有顺序性(时间序列)
- 内存消耗可控(不需要递归栈)
- 更容易理解和调试
理解了”迭代”这一本质,下面进入for循环的具体语法。
9.3.1 语法结构
for循环用于遍历序列(列表、元组、字符串等)或其他可迭代对象。
语法: for item in iterable:
# =============================================================================
# 题目:For循环的基本遍历操作
# =============================================================================
# 本代码演示for循环的四种基本用法:
# 1. 遍历列表 - 最常见的场景,处理股票列表
# 2. 遍历字符串 - 处理股票代码
# 3. 遍历范围 - 生成索引或重复操作
# 4. 遍历字典 - 处理键值对数据
# for循环是金融数据处理中最常用的结构,必须熟练掌握
# ==================== 示例1:遍历列表 ====================
stocks = ['中信证券', '国泰君安', '海通证券'] # 定义券商股票列表
for stock in stocks: # 依次取出列表中的每个股票名称
print(f"分析股票: {stock}") # 输出当前处理的股票
# for循环会自动遍历列表,不需要手动管理索引
# ==================== 示例2:遍历字符串 ====================
code = "600519.SH" # 贵州茅台的股票代码
for char in code: # 依次取出字符串中的每个字符
print(char) # 输出当前字符
# 可以用于解析股票代码的各个部分
# ==================== 示例3:遍历范围 ====================
for i in range(5): # 生成0到4的整数序列
print(f"第{i+1}次迭代") # 输出迭代次数(从1开始计数)
# range(5)生成序列[0,1,2,3,4],不包括5
# ==================== 示例4:遍历字典 ====================
stock_info = {'name': '中信证券', 'code': '600030.SH', 'price': 24.78} # 股票信息字典
for key in stock_info: # 依次取出字典的键
print(f"{key}: {stock_info[key]}") # 输出键和对应的值
# 默认遍历字典的键,通过键访问值9.3.2 range()函数
range()生成整数序列,是循环中最常用的工具。
语法: range(start, stop, step)
# =============================================================================
# 题目:使用range函数生成各种整数序列
# =============================================================================
# 本代码演示range函数的四种常用模式:
# 1. 指定结束值 - 最简单的用法
# 2. 指定起始和结束值 - 生成特定区间
# 3. 指定步长 - 生成等差序列
# 4. 负步长 - 倒序遍历
# range函数在金融计算中用于生成索引、时间范围等
# ==================== 用法1:指定结束值(不包含) ====================
for i in range(5): # 生成0,1,2,3,4
print(f"i = {i}") # 输出: 0,1,2,3,4
# range(stop)生成0到stop-1的整数
# ==================== 用法2:指定起始和结束值 ====================
for year in range(2020, 2025): # 生成2020到2024
print(f"年份: {year}") # 输出: 2020,2021,2022,2023,2024
# range(start,stop)生成start到stop-1的整数
# 常用于遍历特定年份的财务数据
# ==================== 用法3:指定步长 ====================
for i in range(0, 10, 2): # 生成0,2,4,6,8
print(f"偶数: {i}") # 输出偶数序列
# range(start,stop,step)以步长step生成序列
# 常用于跳过某些数据或按固定间隔采样
# ==================== 用法4:负步长(倒序) ====================
for i in range(10, 0, -1): # 生成10,9,8,...,1
print(f"倒计时: {i}") # 输出递减序列
# 负步长用于倒序遍历,如从最新到最旧的数据
# ==================== 金融应用:生成日历日 ====================
import numpy as np # 导入NumPy库
dates = np.arange('2024-01-01', '2024-01-06', dtype='datetime64[D]') # 生成日期序列
print("\n2024年1月前5个日历日:") # 输出标题
for date in dates: # 遍历日期序列
print(f" {date}") # 输出每个日期
# np.arange生成日期范围,常用于时间序列分析9.4 While循环
while循环在条件为True时重复执行,适用于不确定迭代次数的情况。
语法: while condition:
# =============================================================================
# 题目:While循环模拟价格监控过程
# =============================================================================
# 本代码演示while循环的实际应用:
# 1. 简单计数 - 基础用法,理解循环机制
# 2. 价格监控模拟 - 模拟股票价格达到目标价的过程
# while循环适合处理不确定次数的重复任务
# 在金融交易中用于监控实时价格、等待特定条件触发
# ==================== 示例1:简单计数 ====================
count = 0 # 初始化计数器
while count < 5: # 当计数器小于5时持续循环
print(f"计数: {count}") # 输出当前计数值
count += 1 # 重要:更新循环变量,避免无限循环
# 循环执行5次后,count变为5,条件不再满足,退出循环
# ==================== 示例2:模拟价格监控 ====================
print("\n价格监控模拟:") # 输出标题
price = 18.00 # 初始价格
target = 20.00 # 目标价格
days = 0 # 天数计数器
while price < target: # 当价格低于目标价时持续监控
# 模拟价格变化
price += 0.50 # 每天价格上涨0.50元
days += 1 # 天数加1
print(f"第{days}天, 价格: {price:.2f}元") # 输出每日价格
# 安全措施:避免无限循环
if days > 100: # 如果监控超过100天
print("达到最大监控天数") # 输出警告
break # 强制退出循环
# break语句立即终止循环,不管条件是否满足
print(f"\n达到目标价20元, 耗时{days}天") # 输出结果
# 实际应用中,while循环常用于实时监控市场数据重要警告: while循环容易陷入无限循环,必须确保:
- 循环条件最终会变为False
- 循环体内有更新条件的语句
- 添加安全措施(如最大迭代次数)
9.5 循环控制语句
9.5.1 break跳出循环
任务要求:已知 8 个交易日的涨跌幅列表 r_list,用 for 循环逐日遍历,一旦发现当日跌幅超过 1%(i < -0.01)立即 break 跳出,并在循环结束后输出触发预警时的涨跌幅值 i。请将代码原样输入教学平台(注释除外),判定以平台为准。
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
r_list=[0.0192,-0.0001,0.006,0.0074,-0.0127,-0.0067,0.0095,-0.0095] # 定义每日涨跌幅列表
for i in r_list: # 逐日遍历涨跌幅数据
if i<-0.01: # 判断当日跌幅是否超过1%
break # 发现跌幅超限,立即跳出循环
print('涨跌幅数据',i) # 输出触发跌幅预警时的涨跌幅值预期输出(本机 Python 实际运行结果,具体以平台运行结果为准):
涨跌幅数据 -0.0127
判读要点:-0.0127 是列表中第 5 个元素(索引 4),也是第一个跌幅超过 1% 的交易日,break 使循环在该处立即终止;循环变量 i 在循环外仍可访问,保持的是触发 break 时的值,而不是列表最后一个元素 -0.0095。
代码深度解析:
- break的执行逻辑:
- 立即跳出最内层循环
- 不执行循环体剩余代码
- 继续执行循环后的语句
- 金融应用场景:
- 止损触发: 价格触及止损位立即平仓
- 异常检测: 发现异常数据停止处理
- 目标达成: 达到盈利目标退出
- 变量作用域:
- 循环变量在循环外仍然可访问
i保持循环中的最后一个值- 这是Python的特性(不是bug)
9.5.2 continue跳过当前迭代
# =============================================================================
# 题目:使用continue语句跳过特定数据
# =============================================================================
# 本代码演示continue语句的使用:
# 场景:只处理正收益的交易日,跳过负收益日
# continue语句用于过滤不需要处理的数据
# 在金融分析中常用于筛选特定条件的数据
# ==================== 定义收益率数据 ====================
returns = [0.0192, -0.0001, 0.006, 0.0074, -0.0127, 0.0095]
# 6个交易日的收益率数据
# ==================== 只处理正收益交易日 ====================
print("正收益交易日:") # 输出标题
count = 0 # 正收益日计数器
for r in returns: # 遍历每个收益率
# 跳过负收益
if r < 0: # 如果收益率为负
continue # 跳过本次迭代剩余代码,直接进入下一次迭代
# continue之后的代码不会被执行
count += 1 # 正收益日计数加1
print(f" 第{count}个正收益日: {r:.2%}") # 输出正收益率
# :.2%格式化为百分比,保留2位小数
print(f"\n总计正收益日: {count}天") # 输出正收益日总数
# 输出: 4天(跳过了2个负收益日)
# continue只跳过当前迭代,循环继续执行9.5.3 break vs continue
| 关键字 | 作用 | 后续代码 | 循环继续 |
|---|---|---|---|
break |
跳出循环 | 不执行,从循环后继续 | 否 |
continue |
跳过本次 | 不执行,从下次迭代继续 | 是 |
9.6 循环中的else子句
Python的循环可以带else子句,在循环正常结束时执行(未被break中断)。
# =============================================================================
# 题目:循环else子句的两种执行情况
# =============================================================================
# 本代码演示循环else子句的使用:
# 1. 循环正常结束 - 执行else子句
# 2. 循环被break中断 - 不执行else子句
# else子句用于区分"正常结束"和"中断退出"两种情况
# 在金融数据验证中非常有用
# ==================== 场景1:循环正常结束 ====================
prices = [10, 11, 12, 13, 14] # 价格列表(都低于20)
for price in prices: # 遍历价格
if price > 20: # 检查是否有价格超过20
print("发现高价股票") # 发现高价
break # 跳出循环
else: # 循环未被break中断,执行else
print("所有股票价格都在合理范围") # 所有价格都合理
# 输出: "所有股票价格都在合理范围"
print("\n" + "="*50 + "\n") # 输出分隔线
# ==================== 场景2:循环被break中断 ====================
prices2 = [10, 11, 25, 13, 14] # 价格列表(包含25)
for price in prices2: # 遍历价格
if price > 20: # 检查是否有价格超过20
print("发现高价股票") # 发现高价(25)
break # 跳出循环
else: # 由于break被执行,else不会执行
print("所有股票价格都在合理范围")
# 输出: "发现高价股票" (else子句未执行)金融应用: 验证所有数据是否符合条件
# =============================================================================
# 题目:使用循环else验证数据完整性
# =============================================================================
# 本代码演示如何使用循环else进行数据验证:
# 场景:验证所有价格是否为正数
# 如果发现任何异常价格(<=0),立即中断并提示
# 如果所有价格都正常,输出验证通过的提示
# ==================== 定义价格数据 ====================
prices = [24.78, 20.15, 14.03, 22.41, 16.17] # 股票价格列表
# 所有价格都是正数
# ==================== 验证所有价格是否为正数 ====================
for price in prices: # 遍历每个价格
if price <= 0: # 检查是否有非正价格
print(f"发现异常价格: {price}") # 输出异常价格
break # 发现异常,立即中断
else: # 循环正常结束(未发现异常)
print("✓ 所有价格数据有效") # 输出验证通过信息
# 输出: "✓ 所有价格数据有效"
# 这种模式在金融数据清洗中非常常用9.7 实战案例股价涨跌幅监控
# =============================================================================
# 题目:股价涨跌幅实时监控系统
# =============================================================================
# 本代码实现一个完整的股价监控系统:
# 1. 模拟一周的股价数据
# 2. 逐日计算涨跌幅
# 3. 根据涨跌幅判断市场状态
# 4. 触发预警时自动停止监控
# 这个系统综合运用了for循环、if条件判断、break语句等核心概念
# 是实际量化交易系统的基础框架
# ==================== 导入NumPy库 ====================
import numpy as np # 导入用于生成随机数和数值计算
# ==================== 模拟一周的股价数据 ====================
np.random.seed(42) # 设置随机种子,确保结果可重现
base_price = 20.0 # 起始价格
price_changes = np.random.randn(5) * 0.5 # 生成5日价格变化(标准差0.5)
prices = base_price + np.cumsum(price_changes) # 累加变化得到价格序列
# np.cumsum计算累积和,模拟价格的随机游走
# ==================== 输出系统标题 ====================
print("=" * 60) # 输出分隔线
print("股价涨跌幅监控系统") # 输出系统名称
print("=" * 60) # 输出分隔线
# ==================== 设定预警阈值 ====================
alert_threshold = -0.01 # 跌幅超过-1%触发警报
# ==================== 逐日监控股价 ====================
for i, price in enumerate(prices, 1): # 遍历价格序列,i从1开始
# 计算涨跌幅
if i == 1: # 第一天没有前一日数据
daily_return = 0 # 涨跌幅设为0
else: # 其他天数
daily_return = (price - prices[i-2]) / prices[i-2] # 计算收益率
# prices[i-2]是前一日价格(因为enumerate从1开始,i-2才是正确的索引)
# 转换为百分比
return_pct = daily_return * 100 # 转换为百分比形式
# 判断市场状态
if daily_return < alert_threshold: # 跌幅超过阈值
status = "⚠️ 警告" # 风险警告状态
action = "考虑减仓" # 建议减仓
elif daily_return > 0.01: # 涨幅超过1%
status = "✓ 上涨" # 上涨状态
action = "继续持有" # 建议持有
else: # 正常波动范围
status = "正常" # 正常状态
action = "观望" # 建议观望
# 输出每日监控报告
print(f"\n第{i}天:") # 输出天数
print(f" 收盘价: {price:.2f}元") # 输出收盘价
print(f" 涨跌幅: {return_pct:+.2f}%") # 输出涨跌幅(+表示正号)
print(f" 状态: {status}") # 输出状态
print(f" 建议: {action}") # 输出操作建议
# 检查是否需要停止监控
if daily_return < alert_threshold: # 如果触发预警
print("\n" + "=" * 60) # 输出分隔线
print("触发风险预警,系统暂停监控") # 输出预警信息
print("=" * 60) # 输出分隔线
break # 立即停止监控
else: # 循环正常结束(未触发break)
# 循环正常结束(未触发警报)
print("\n" + "=" * 60) # 输出分隔线
print("一周监控结束,市场表现平稳") # 输出总结信息
print("=" * 60) # 输出分隔线
# for-else结构:正常结束时执行else,break中断时不执行9.8 嵌套循环
在处理二维数据(如多只股票多日收益)时,需要使用嵌套循环。
# =============================================================================
# 题目:嵌套循环统计每日上涨股票数量
# =============================================================================
# 本代码演示嵌套循环的使用:
# 场景:3只股票,5个交易日的收益率数据
# 任务:统计每个交易日有多少只股票上涨
# 嵌套循环用于处理二维数据结构(外层遍历天数,内层遍历股票)
# 这是金融数据分析中的常见模式
# ==================== 定义收益率数据 ====================
returns = [
[0.01, 0.02, -0.01, 0.03, 0.02], # 股票A的5日收益率
[0.02, -0.01, 0.03, 0.01, -0.02], # 股票B的5日收益率
[-0.01, 0.03, 0.02, -0.02, 0.01] # 股票C的5日收益率
]
# 这是一个3行5列的二维列表(矩阵)
stock_names = ['股票A', '股票B', '股票C'] # 股票名称列表
# ==================== 统计每日正收益股票 ====================
print("每日正收益股票统计:\n") # 输出标题
for day in range(5): # 外层循环:遍历5个交易日
positive_count = 0 # 正收益股票计数器
positive_stocks = [] # 正收益股票名称列表
for stock_idx in range(3): # 内层循环:遍历3只股票
ret = returns[stock_idx][day] # 获取第stock_idx只股票在第day天的收益率
# returns[stock_idx]是第stock_idx只股票的所有收益率
# [day]取出该股票在第day天的收益率
if ret > 0: # 如果收益率为正
positive_count += 1 # 计数器加1
positive_stocks.append(stock_names[stock_idx]) # 添加股票名称
# 输出当日统计结果
print(f"第{day+1}天: {positive_count}只股票上涨") # 输出上涨股票数量
print(f" 上涨股票: {', '.join(positive_stocks)}") # 输出上涨股票名称
# ', '.join()将列表元素用逗号连接成字符串性能提示: 嵌套循环时间复杂度为O(n×m),数据量大时应考虑使用NumPy向量化操作。
9.9 循环性能优化
# =============================================================================
# 题目:不同循环方法的性能对比测试
# =============================================================================
# 本代码演示四种数组相加方法的性能差异:
# 1. Python for循环 - 最慢,但最易懂
# 2. 列表推导 - 较快,更Pythonic
# 3. zip+列表推导 - 更Pythonic,代码更清晰
# 4. NumPy向量化 - 最快,推荐用于大数据
# 性能差异在大数据集上非常明显,NumPy可快数百倍
# 在量化交易中,性能优化直接影响策略回测速度
# ==================== 导入时间库 ====================
import time # 用于计时
# ==================== 生成大数据集 ====================
n = 100000 # 数据规模:10万个元素
list1 = list(range(n)) # 生成列表[0,1,2,...,99999]
list2 = list(range(n)) # 生成相同的列表
# ==================== 方法1:Python for循环(慢) ====================
start = time.time() # 记录开始时间
result_loop = [] # 存储结果
for i in range(n): # 遍历所有元素
result_loop.append(list1[i] + list2[i]) # 相加并添加到结果列表
loop_time = time.time() - start # 计算耗时
# for循环最慢,因为每次迭代都有Python解释器开销
# ==================== 方法2:列表推导(较快) ====================
start = time.time() # 记录开始时间
result_listcomp = [list1[i] + list2[i] for i in range(n)] # 列表推导
listcomp_time = time.time() - start # 计算耗时
# 列表推导比for循环快,因为内部优化
# ==================== 方法3:zip+列表推导(更Pythonic) ====================
start = time.time() # 记录开始时间
result_zip = [a + b for a, b in zip(list1, list2)] # 使用zip配对
zip_time = time.time() - start # 计算耗时
# zip函数将两个列表的元素配对,代码更清晰
# ==================== 方法4:NumPy向量化(最快) ====================
import numpy as np # 导入NumPy
arr1 = np.array(list1) # 转换为NumPy数组
arr2 = np.array(list2) # 转换为NumPy数组
start = time.time() # 记录开始时间
result_numpy = arr1 + arr2 # 直接相加(向量化操作)
numpy_time = time.time() - start # 计算耗时
# NumPy使用C语言实现向量化操作,速度极快
# ==================== 输出性能对比结果 ====================
print(f"Python循环: {loop_time:.4f}秒") # 输出循环耗时
print(f"列表推导: {listcomp_time:.4f}秒") # 输出列表推导耗时
print(f"zip方法: {zip_time:.4f}秒") # 输出zip方法耗时
print(f"NumPy向量化: {numpy_time:.4f}秒") # 输出NumPy耗时
print(f"\n最快方法比最慢快 {loop_time/numpy_time:.1f}倍") # 输出性能倍数
# 在大数据集上,NumPy通常比纯Python循环快100-1000倍
# 这就是为什么量化交易必须使用NumPy/Pandas的原因9.10 本章小结
要点:
for适合遍历已知序列,while适合条件驱动的迭代;range(start, stop, step)含头不含尾break立即跳出最内层循环,continue只结束本次迭代for-else/while-else的else子句只在循环未被break打断时执行- 循环变量在循环结束后仍可访问,保持最后一次使用的值
- 批量任务优先选择列表推导式或 NumPy 向量化,大规模数据下性能差距可达百倍以上
易错点:
while的条件更新语句写错,会造成死循环或循环体一次都不执行- 嵌套循环中
break只跳出最内层,误以为它会终止全部循环 - 循环体从未执行时就使用循环变量,会触发
NameError - 在遍历列表的同时增删其元素,导致元素被跳过或重复处理
9.11 动手与思考
以下练习每题附参考答案(默认折叠)。请先独立完成并写下你的判断,再点开对照,最后上机验证。
输出预测:不运行代码,先写出下面代码的输出结果,再上机检验你的判断。
r_list = [0.0192, -0.0001, 0.006, 0.0074, -0.0127, -0.0067, 0.0095, -0.0095] for i in r_list: if i < -0.01: break else: print("无跌幅超限日") print(i) total = 0 k = 0 while k < len(r_list): total += r_list[k] k += 1 print(round(total, 4))参考答案(先写下你的预测再点开)
解题思路:逐段推演。第一段 for 循环从左到右遍历:
0.0192、-0.0001、0.006、0.0074都不满足i < -0.01;轮到第 5 个元素-0.0127时条件成立,执行break立即跳出。因为循环是被break中断而非正常跑完,for-else的else子句不执行,屏幕上不会出现“无跌幅超限日”。循环结束后i仍可访问,保持触发break时的值,输出-0.0127(而不是列表末尾的-0.0095)。第二段 while 循环从k = 0累加到k = 7,把 8 个涨跌幅全部累进total,和为0.0131(浮点累加有微小误差,round(total, 4)后恰为0.0131),输出0.0131。# 验证脚本:for-else的break行为与while累加求和 r_list = [0.0192, -0.0001, 0.006, 0.0074, -0.0127, -0.0067, 0.0095, -0.0095] # 8日涨跌幅列表 for i in r_list: # 逐日遍历 if i < -0.01: # 跌幅超过1% break # 立即跳出,else不执行 else: # 仅当循环未被break打断才执行 print('无跌幅超限日') print(i) # i保持触发break时的值 total = 0 # 累加器清零 k = 0 # 下标从0开始 while k < len(r_list): # 未遍历完则继续 total += r_list[k] # 累加当日涨跌幅 k += 1 # 下标前移(漏写会死循环) print(round(total, 4)) # 保留4位小数输出总和预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
-0.0127 0.0131回扣本章:对应本章小结“要点”第 3、4 条——
else子句只在循环未被break打断时执行,循环变量在循环结束后保持最后一次使用的值。概念辨析:
break与continue对循环流程的影响有何不同?上面第 1 题中else子句为什么没有输出,怎样的数据才会让它执行?再说明:for与while各自适用的场景,以及”循环结束后i的值”由什么决定。参考答案(点开前请先独立完成)
解题思路:逐点作答。第一,
break立即终止整个循环(跳出最内层循环体,执行循环之后的语句),而continue只结束本次迭代,跳过本轮剩余语句、直接进入下一轮,循环本身继续。第二,第 1 题中else没有输出是因为循环在第 5 个元素处被break中断——for-else的else只在序列被完整遍历、没有触发break时执行;只有当r_list中所有元素都满足i >= -0.01(如[0.01, -0.005, 0.008],本机实测)时,循环正常跑完,else才会输出提示。第三,for适用于遍历已知的有限序列(列表、range、字典等),迭代次数由序列长度决定;while适用于迭代次数事先未知、由条件驱动的场景(如“价格未达目标价就继续监控”),但要保证循环体内有推动条件变化的语句,否则死循环。第四,循环结束后i的值由“最后一次赋给它的元素”决定:正常结束就是序列最后一个元素;被break中断就是触发break那一轮的元素(第 1 题即-0.0127);若循环体一次都没执行(如空列表),i从未被赋值,循环外使用会抛NameError。回扣本章:对应本章小结“要点”第 1、2、3、4 条与“易错点”第 1、3 条——
break/continue的差别、else触发条件、for/while适用场景与循环变量的残留值。变式任务(平台任务同型改造):沿用平台任务的
r_list,把break改成continue,统计跌幅超过 1% 的交易日天数与它们的涨跌幅值;再用while循环改写平台任务,输出第一个跌幅超过 1% 的交易日是第几个交易日(从 1 开始计数);最后用for-else改写,当全月没有跌幅超过 1% 的交易日时输出”本月无跌幅预警”。参考答案(点开前请先独立完成)
解题思路:三段各自改写。第一段把
break换成continue:对不满足“跌幅超过 1%”的日子执行continue跳过,满足的累计入列表,循环跑完全程——r_list中只有-0.0127一个超限值(-0.0067、-0.0095均大于 -0.01),故天数为 1。第二段用while加下标从 0 扫描,找到第一个满足r_list[day] < -0.01的下标即输出“第几个交易日”(下标加 1 转为从 1 计数),-0.0127在下标 4,即第 5 个交易日。第三段for-else:把预警输出放在循环体、break中断,把“无预警”提示放在else,全月无超限日时循环正常结束、else输出“本月无跌幅预警”;演示时另用一组无超限日的数据r_mild触发该分支(r_list本身有超限日,走的是break分支)。# 变式程序:continue统计、while定位首个超限日与for-else无预警判断 r_list = [0.0192, -0.0001, 0.006, 0.0074, -0.0127, -0.0067, 0.0095, -0.0095] # 沿用平台任务数据 drop_days = [] # 存放跌幅超过1%的涨跌幅值 for i in r_list: # 逐日遍历 if i >= -0.01: # 未超限的日子 continue # 跳过本轮,继续下一日 drop_days.append(i) # 超限日记录其涨跌幅 print('跌幅超限天数', len(drop_days)) # 输出超限天数 print('对应涨跌幅', drop_days) # 输出超限日的涨跌幅值 day_index = 0 # 交易日下标(从0开始) while day_index < len(r_list): # 未扫描完则继续 if r_list[day_index] < -0.01: # 找到第一个跌幅超限日 print('第一个跌幅超限日是第', day_index + 1, '个交易日') # 下标加1转为从1计数 break # 找到即退出 day_index += 1 # 未找到,检查下一日 r_mild = [0.01, -0.005, 0.008, -0.009] # 全月无跌幅超过1%的示例数据 for i in r_mild: # 遍历全月数据 if i < -0.01: # 检查是否跌幅超限 print('发现跌幅预警', i) # 输出预警值并break break else: # 循环未被break打断,说明全月无超限 print('本月无跌幅预警')预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
跌幅超限天数 1 对应涨跌幅 [-0.0127] 第一个跌幅超限日是第 5 个交易日 本月无跌幅预警注意:以上为本题变式的独立代码;列表 9.4 对应平台任务的原始代码块仍须原样输入教学平台,不要用本变式替换。
回扣本章:对应本章小结“要点”第 1、2、3 条——
continue只结束本次迭代、while适合条件驱动的扫描、for-else的else只在未触发break时执行。动手验证:分别用 for 循环、列表推导式与 NumPy 向量化三种方式,把
r_list中小于 0 的涨跌幅替换为 0 并输出结果,确认三者结果一致;再用time模块对 100 万个随机数做同样处理,比较三种方式的耗时。参考答案(点开前请先独立完成)
解题思路:正确性部分,三种写法逐元素执行同一规则“负值置 0、非负保留”:for 循环逐个判断后
append;列表推导式[0 if i < 0 else i for i in r_list]一行完成;NumPy 用np.where(r_array < 0, 0, r_array)对整个布尔条件数组一次性选择。三者数值完全一致(列表里 0 显示为0,NumPy 数组里显示为0.,显示形式不同、值相同)。性能部分对 100 万个标准正态随机数重复同样处理:纯 Python for 循环要执行 100 万次解释器层面的判断与append,列表推导省去重复的append调用略快,NumPy 向量化把整个循环下沉到 C 层一次完成,通常快一个数量级以上;具体耗时随机器与当时负载波动,应以量级对比为准。# 验证脚本(一):三种写法的正确性对比 import numpy as np # 导入NumPy库 r_list = [0.0192, -0.0001, 0.006, 0.0074, -0.0127, -0.0067, 0.0095, -0.0095] # 8日涨跌幅 result_loop = [] # 存放for循环结果 for i in r_list: # 逐日遍历 if i < 0: # 负涨跌幅 result_loop.append(0) # 替换为0 else: result_loop.append(i) # 非负保留 print(result_loop) # 输出for循环结果 result_comp = [0 if i < 0 else i for i in r_list] # 列表推导式一行完成 print(result_comp) # 输出列表推导结果 r_array = np.array(r_list) # 转为NumPy数组 print(np.where(r_array < 0, 0, r_array)) # 向量化条件替换预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准):
[0.0192, 0, 0.006, 0.0074, 0, 0, 0.0095, 0] [0.0192, 0, 0.006, 0.0074, 0, 0, 0.0095, 0] [0.0192 0. 0.006 0.0074 0. 0. 0.0095 0. ]# 验证脚本(二):100万随机数上三种方式的耗时对比 import numpy as np # 导入NumPy库 import time # 导入时间模块用于计时 np.random.seed(42) # 固定随机种子保证可复现 big_returns = np.random.randn(1000000) # 生成100万个随机数 start = time.time() # 记录for循环开始时间 loop_result = [] # 存放循环结果 for v in big_returns: # 逐元素循环 loop_result.append(0 if v < 0 else v) # 负值置0 loop_cost = time.time() - start # for循环耗时 start = time.time() # 记录列表推导开始时间 comp_result = [0 if v < 0 else v for v in big_returns] # 列表推导批量处理 comp_cost = time.time() - start # 列表推导耗时 start = time.time() # 记录向量化开始时间 numpy_result = np.where(big_returns < 0, 0, big_returns) # NumPy向量化处理 numpy_cost = time.time() - start # 向量化耗时 print('for循环耗时 {:.4f} 秒'.format(loop_cost)) # 输出各项耗时 print('列表推导耗时 {:.4f} 秒'.format(comp_cost)) print('NumPy向量化耗时 {:.4f} 秒'.format(numpy_cost)) print('NumPy 比循环快 {:.1f} 倍'.format(loop_cost / numpy_cost)) # 加速比 print('三法结果一致', np.allclose(loop_result, comp_result) and np.allclose(loop_result, numpy_result)) # 校验一致预期输出(本机 peter 环境实际运行结果,具体以平台运行结果为准;耗时随机器与负载波动,量级关系稳定):
for循环耗时 0.1226 秒 列表推导耗时 0.0868 秒 NumPy向量化耗时 0.0078 秒 NumPy 比循环快 15.8 倍 三法结果一致 True回扣本章:对应本章小结“要点”第 5 条——批量任务优先选择列表推导式或 NumPy 向量化,大规模数据下性能差距可达十倍到百倍以上。